# this script walks through the steps needed to determine the RC time
#   constant and passive leak conductance of a cell from hyperpolarization
#   voltage traces.

import scipy
from numpy import histogram
from matplotlib.figure import Figure
from matplotlib.widgets import SpanSelector
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from genutils import io
from genutils import special_functions
from genutils import handy_functions

import Tkinter as Tk
import tkFileDialog
import tkSimpleDialog

import os

class empty:
    # an empty class definition to store global variables in.
    pass

def calc_ylines(data,bins):
    # this function returns the two most represented y values given some data and a number of bins
    histo = histogram(data,bins=bins)
    # the index of the maximum value in the FIRST half of histo
    first_max_index = histo[0][:len(histo[0])/2].argsort()[-1]
    # the index of the maximum value in the LAST half of histo
    last_max_index = histo[0][len(histo[0])/2:].argsort()[-1]+len(histo[0])/2

    # the two steady state voltages are then histo[1] at those two indexes
    y_min = histo[1][first_max_index]
    y_max = histo[1][last_max_index]
    return y_min,y_max

def open_file(event):
    # method to ask user to open file and then do it.
    program_data.filename = tkFileDialog.askopenfilename(filetypes=[('Data Files','*.dat'),('Data Files','*.DAT'),('All Files','*')])
    program_data.file = open(program_data.filename)
    # open the Data_file_dialog to prompt the user to pick some columns
    Data_file_dialog(root)

class Data_file_dialog(tkSimpleDialog.Dialog):
    # this is the pop up dialog that prompts the user to choose the columns of the data file
    def body(self,master):
        # create the column entry fields and labels
        Tk.Label(master,text='Time series column (0,1,2,...): ').grid(row=1,column=0,sticky=Tk.E)
        Tk.Label(master,text='Voltage trace column (0,1,2,...): ').grid(row=2,column=0,sticky=Tk.E)

        self.e1 = Tk.Entry(master)
        self.e2 = Tk.Entry(master)

        self.e1.grid(row=1,column=1,sticky=Tk.W)
        self.e2.grid(row=2,column=1,sticky=Tk.W)

        # create the text preview area
        # make a text area and display the first 5 lines of text in it
        text_area = Tk.Text(master,setgrid=True)
        # display the first 5 lines of text in the text area and make the area disabled
        text_area.configure(state=Tk.NORMAL)
        for line in program_data.file.readlines()[:20]:
            text_area.insert(Tk.END,line)
        text_area.configure(state=Tk.DISABLED)
        text_area.grid(row=0,column=0,columnspan=2)
        
        return self.e1 # this will have the initial focus

    def apply(self):
        # this method gets called when the user selects ok or presses <ENTER>
        #   but not if they select cancel or press <ESC>
        program_data.time_column = int(self.e1.get())
        program_data.voltage_column = int(self.e2.get())

program_data = empty() # we will store global data in here to avoid namespace clutter
    
# open up the main application window
root = Tk.Tk()
root.wm_title("Fitting Hyperpolarization Data    (By David Morton - July 2008)")
open_file(True)

# read in the data columns
data = scipy.array(io.raw_data_columns(program_data.filename))
v = data.T[program_data.voltage_column]
t = data.T[program_data.time_column]

#-------------- MAIN PLOT --------------------------
main_fig = empty()
main_fig.fig = Figure(figsize=(7,3))
main_fig.canvas = FigureCanvasTkAgg(main_fig.fig, master=root)
main_fig.ax = main_fig.fig.add_subplot(111)
main_fig.ax.set_title('Raw Data\nSelect the region to fit.')
main_fig.fig.set_edgecolor('#FFFFFF')
main_fig.fig.set_facecolor('#FFFFFF')
main_fig.fig.subplots_adjust(top=0.80,bottom=0.1,left=0.1,right=0.9)
main_fig.ax.plot(t,v)

#-------------- FALLING FIT PLOT --------------------------
falling_fig = empty()
falling_fig.fig = Figure(figsize=(3,3))
falling_fig.canvas = FigureCanvasTkAgg(falling_fig.fig, master=root)
falling_fig.ax = falling_fig.fig.add_subplot(111)
falling_fig.ax.set_title('Exponential Fit 1')
falling_fig.fig.set_edgecolor('#FFFFFF')
falling_fig.fig.set_facecolor('#FFFFFF')
falling_fig.fig.subplots_adjust(top=0.85,bottom=0.15,left=0.15,right=0.9)
falling_fig.fit_line, = falling_fig.ax.plot(t,v,lw=3,color='y')
falling_fig.data_line, = falling_fig.ax.plot(t,v)
falling_fig.ax.set_xlim(t[0],t[int(len(t)/2)])

#-------------- RISING FIT PLOT --------------------------
rising_fig = empty()
rising_fig.fig = Figure(figsize=(3,3))
rising_fig.canvas = FigureCanvasTkAgg(rising_fig.fig, master=root)
rising_fig.ax = rising_fig.fig.add_subplot(111)
rising_fig.ax.set_title('Exponential Fit 2')
rising_fig.fig.set_edgecolor('#FFFFFF')
rising_fig.fig.set_facecolor('#FFFFFF')
rising_fig.fig.subplots_adjust(top=0.85,bottom=0.15,left=0.15,right=0.9)
rising_fig.fit_line, = rising_fig.ax.plot(t,v,lw=3,color='y')
rising_fig.data_line, = rising_fig.ax.plot(t,v)
rising_fig.ax.set_xlim(t[int(len(t)/2)],t[-1])

#-------------- RADIO BUTTONS -----------------------------
# the functions called when the radio buttons are pressed.
def set_fit_falling():
    falling_span.visible = True
    rising_span.visible = False
    dv_span.visible = False

def set_fit_rising():
    falling_span.visible = False
    rising_span.visible = True
    dv_span.visible = False

def set_adjust_dv():
    falling_span.visible = False
    rising_span.visible = False
    dv_span.visible = True

# the radio buttons themselves.
buttons = []
button_selected = Tk.IntVar()
buttons.append( Tk.Radiobutton( root, text='Adjust dV', \
                variable=button_selected,value=1,indicatoron=0,\
                padx=10,pady=10,command=set_adjust_dv ))
buttons.append( Tk.Radiobutton( root, text='Fit Falling', \
                variable=button_selected,value=2,indicatoron=0,\
                padx=10,pady=10,command=set_fit_falling ))
buttons.append( Tk.Radiobutton( root, text='Fit Rising', \
                variable=button_selected,value=3,indicatoron=0,\
                padx=10,pady=10,command=set_fit_rising ))

#-------------- SPAN SELECTORS -----------------------------
# the functions called when the span selectors are used.
def fit_selection(t_start,t_end):
    # fit an exponential to the data and plot the results
    exp_parameters = handy_functions.exp_fit(t,v,t_start,t_end)
    scale = exp_parameters[0]
    xoff = exp_parameters[1]
    yoff = exp_parameters[2]
    tau = exp_parameters[3]
    t_fit = scipy.linspace(t_start,t_end,100) # 100 pts from t_start to t_end
    v_fit = special_functions.exp_decay(scale,xoff,yoff,tau,t_fit)
    return t_fit, v_fit, tau

def fit_falling(t_start,t_end):
    # fit the falling exponential and then display it.
    t_fit, v_fit, tau = fit_selection(t_start,t_end)
    program_data.tau1 = tau
    falling_fig.ax.set_xlim(t_start,t_end)
    # set the data for the fit line
    falling_fig.fit_line.set_data(t_fit,v_fit)
    falling_fig.fig.canvas.draw()

def fit_rising(t_start,t_end):
    # fit the rising exponential and then display it.
    t_fit, v_fit, tau = fit_selection(t_start,t_end)
    program_data.tau2 = tau
    rising_fig.ax.set_xlim(t_start,t_end)
    # set the data for the fit line
    rising_fig.fit_line.set_data(t_fit,v_fit)
    rising_fig.fig.canvas.draw()

def adjust_dv(v_min,v_max):
    # adjust the horizontal lines on the main figure.
    program_data.v_min = v_min
    program_data.v_max = v_max
    del main_fig.ax.lines[-1]
    del main_fig.ax.lines[-1]
    main_fig.v_min_line = main_fig.ax.axhline(y=program_data.v_min,linewidth=1,color='r')
    main_fig.v_max_line = main_fig.ax.axhline(y=program_data.v_max,linewidth=1,color='r')
    main_fig.fig.canvas.draw()

# the span selectors themselves.
falling_span = SpanSelector(main_fig.ax,fit_falling,'horizontal',useblit=True,\
                    rectprops=dict(alpha=0.5,facecolor='blue') )
rising_span = SpanSelector(main_fig.ax,fit_rising,'horizontal',useblit=True,\
                    rectprops=dict(alpha=0.5,facecolor='blue') )
dv_span = SpanSelector(main_fig.ax,adjust_dv,'vertical',useblit=True,\
                    rectprops=dict(alpha=0.5,facecolor='red') )


#---------------- CALCULATE BUTTON ------------------------
def calc_cell():
    # calculate the cell properties and display the results.
    program_data.current = float(current_entry.get() )
    program_data.avg_tau = (program_data.tau1 + program_data.tau2)/2
    program_data.dv = abs(program_data.v_min - program_data.v_max)
    program_data.g = program_data.current/program_data.dv
    # knowing R = 1/g we can find C knowing that tau=RC
    program_data.C = program_data.avg_tau * program_data.g
    line1 = "Vmin: %2.2f   Vmax: %2.2f   dV: %2.2f\n" % (program_data.v_min, program_data.v_max, program_data.dv)
    line2 = "Conductance: %2.6f uS\n" % (program_data.g)
    line3 = "Capacitance: %2.6f nF\n" % (program_data.C)
    line4 = "Tau1: %2.2f    Tau2: %2.2f  Avg: %2.2f" %\
             (program_data.tau1,program_data.tau2,program_data.avg_tau)
    # delete the text in the text area and put new stuff back.
    calc_text_area.delete(1.0,Tk.END)
    calc_text_area.insert(Tk.END, line1+line2+line3+line4 )
    
current_label = Tk.Label(root, text='Current (nA):')
current_entry = Tk.Entry(root)
current_entry.insert(Tk.END,'0.02')
calc_button = Tk.Button(master=root, text="Calculate Cell Properties", command=calc_cell)
calc_text_area = Tk.Text(root,height=5,width=40)

#---------------- GRID LAYOUT ----------------------------
main_fig.canvas.get_tk_widget().grid(row=1,column=3,rowspan=3,columnspan=2)
falling_fig.canvas.get_tk_widget().grid(row=10,column=3)
rising_fig.canvas.get_tk_widget().grid(row=10,column=4)
buttons[0].grid(row=1,column=1,columnspan=2) # adjust dv button
buttons[1].grid(row=11,column=3) # fit_falling button
buttons[2].grid(row=11,column=4) # fit_rising button
current_label.grid(row=2,column=1,sticky=Tk.E)
current_entry.grid(row=2,column=2,sticky=Tk.W)
calc_button.grid(row=3,column=1,columnspan=2,sticky=Tk.S) 
calc_text_area.grid(row=10,column=1,columnspan=2,sticky=Tk.N)



#----------------- initialization ------------------------
# initialize the dv lines.
program_data.v_min,program_data.v_max = calc_ylines(v,50)
# plot the maximum steady state voltage and minimum steady state voltage lines
main_fig.v_min_line = main_fig.ax.axhline(y=program_data.v_min,linewidth=1,color='r')
main_fig.v_max_line = main_fig.ax.axhline(y=program_data.v_max,linewidth=1,color='r')
set_adjust_dv()
Tk.mainloop()




 




